//*************************************************************************************************
// OLEHELP.CPP
//
// This file implements a set of helper functions that makes writing OLE classes a little bit
// easier.
//*************************************************************************************************
#include "stdafx.h"						// Precompiled header key.
#include "locks.h"						// Some mutex locking classes.
#include "olehelp.h"					// OLE helper routines.

#include "ssautoi.h"

// Internal prototypes.
static BOOL SetKeyAndValue(LPCTSTR szKey, LPCTSTR szSubkey, LPCTSTR szValue);
static BOOL SetRegValue(LPCTSTR szKeyName, LPCTSTR szValueName, LPCTSTR szValue);
static BOOL DeleteKey(LPCTSTR szKey,LPCTSTR szSubkey);

#ifndef NumItems
#define NumItems(x) (sizeof(x)/sizeof(*x))
#endif

extern CCritLock gsMutex;

//*************************************************************************************************
//*************************************************************************************************
HRESULT CTypeInfo::GetTypeInfo(				// Did we get the typeinfo?
	UINT		iInfo,						// TypeInfo index.
	LCID		lcid,						// Locale.
	ITypeInfo	**ppITypeInfo)				// Type info.
{
	Trace::Write("Enter CTypeInfo::GetTypeInfo");

	ITypeLib	*pITypeLib = NULL;			// Used to load a typelib.
	HRESULT		hr;

	CAutoLock sLock(&gsMutex);

	// Check the reserved parameter.
	if (iInfo != 0)
	{
		return TYPE_E_ELEMENTNOTFOUND;
	}

	// Make sure we were provided a valid out parameter.
	if (ppITypeInfo == NULL)
	{
		return E_POINTER;
	}

	// Check the last typelib requested.
	if (m_pITypeLib == NULL)
	{
		// Load the type library.
		if (FAILED(hr = LoadRegTypeLib(m_TLBID, m_iTypeLibMajorVersion, m_iTypeLibMinorVersion,
							PRIMARYLANGID(lcid), &pITypeLib)))
		{
			switch (PRIMARYLANGID(lcid))
			{
				case LANG_NEUTRAL:
				case LANG_ENGLISH:
				hr = LoadTypeLib(m_szTLB, &pITypeLib);
				break;

				default:
				return DISP_E_UNKNOWNLCID;
			}

			if (FAILED (hr))
			{
				return hr;
			}
		}

		// Fill in the entry.
		m_pITypeLib = pITypeLib;
		m_lcid = lcid;
	}
	// Make sure the languages match.
	else if (PRIMARYLANGID(lcid) != PRIMARYLANGID(m_lcid) &&
			PRIMARYLANGID(lcid) != 0 && PRIMARYLANGID(m_lcid) != 0)
	{
		assert(PRIMARYLANGID(lcid) == PRIMARYLANGID(m_lcid));
		return E_OUTOFMEMORY;
	}

 	// Check if we are returning the most recent typeinfo from the typelib.
	if (m_pITypeInfo == NULL)
	{
		// Look up the typeinfo.
		if (FAILED(hr = m_pITypeLib->GetTypeInfoOfGuid(m_sIID, ppITypeInfo)))
		{
			return hr;
		}

		// Save the typeinfo information.
		m_pITypeInfo = *ppITypeInfo;
	}
	else
	{
		*ppITypeInfo = m_pITypeInfo;
	}

	// AddRef for the user.
	(*ppITypeInfo)->AddRef();
	return S_OK;
}

//*************************************************************************************************
//*************************************************************************************************
HRESULT CTypeInfo::GetIDsOfNames(			// S_OK or error.
	REFIID		sIID,						// Interface ID (future use).
	OLECHAR		**pszNames,					// Names to get IDs of.
	UINT		iNames,						// # of names in list.
	LCID		lcid,						// Locale of the names.
	DISPID		*piDispID)					// Buffer for list of DISPIDs.
{
	Trace::Write("Enter CTypeInfo::GetIDsOfNames");

	ITypeInfo	*pITypeInfo = NULL;			// My TypeInfo.
	HRESULT		hr;

	// The IID must be NULL.
	if (sIID != IID_NULL)
	{
		return DISP_E_UNKNOWNINTERFACE;
	}

	// Get the TypeInfo.
	if (SUCCEEDED(hr = GetTypeInfo(0, lcid, &pITypeInfo)))
	{
		// Just call GetIDsOfNames on the TypeInfo.
		hr = pITypeInfo->GetIDsOfNames(pszNames, iNames, piDispID);
		pITypeInfo->Release();
	}
	return hr;
}

//*************************************************************************************************
//*************************************************************************************************
HRESULT CTypeInfo::Invoke(					// S_OK or error.
	IDispatch	*pIIface,					// The interface to invoke.
	DISPID		iDispID,					// Dispatch ID we are invoking.
	REFIID		sIID,						// Interface ID.
	LCID		lcid,						// Locale of the names.
	USHORT		iFlags,						// Dispatch flags.
	DISPPARAMS	*psParams,					// Dispatch parameters.
	VARIANT		*psResult,					// Returned result.
	EXCEPINFO	*psExcep,					// Exception info.
	UINT		*piArgErr)					// Index of invalid argument.
{
	Trace::Write("Enter CTypeInfo::Invoke");

	ITypeInfo	*pITypeInfo = NULL;			// My TypeInfo.
	HRESULT		hr;

	// The IID must be NULL.
	if (sIID != IID_NULL)
	{
		return DISP_E_UNKNOWNINTERFACE;
	}

	// Get the TypeInfo.
	if (SUCCEEDED(hr = GetTypeInfo(0, lcid, &pITypeInfo)))
	{
		// Clear the error info.
		SetErrorInfo(0L, NULL);

		// Just call the TypeInfo's Invoke.
		hr = pITypeInfo->Invoke(pIIface, iDispID, iFlags,
								psParams, psResult, psExcep, piArgErr);

		pITypeInfo->Release();
	}
	return hr;
}

//*************************************************************************************************
// Register the basic information in the system registry for a given object class.
//*************************************************************************************************
int RegisterClass(							// -1 or 0.
	REFCLSID	rclsid,						// Class ID we are registering.
	LPCTSTR		szDesc,						// Class description.
	LPCTSTR		szProgID,					// Class prog ID.
	LPCTSTR		szIndepProgID,				// Class version independant prog ID.
	LPCTSTR		szModule)					// Spec for this DLL.
{
	Trace::Write("Enter RegisterClass");

	TCHAR		szID[64];					// The class ID to register.
	TCHAR		szCLSID[64];				// CLSID\\szID.
	TCHAR		rcInproc[256];				// CLSID\\InprocServer32
	int			iStat = 0;					// Build up return statuses.

    // Create some base key strings.
    StringFromGUID2(rclsid, szID, NumItems(szID));
    _tcscpy_s(szCLSID, NumItems(szCLSID), _T("CLSID\\"));
    _tcscat_s(szCLSID, NumItems(szCLSID), szID);

    // Create ProgID keys.
    iStat |= SetKeyAndValue(szProgID, NULL, szDesc);
    iStat |= SetKeyAndValue(szProgID, _T("CLSID"), szID);

    // Create VersionIndependentProgID keys.
    iStat |= SetKeyAndValue(szIndepProgID, NULL, szDesc);
    iStat |= SetKeyAndValue(szIndepProgID, _T("CurVer"), szProgID);
    iStat |= SetKeyAndValue(szIndepProgID, _T("CLSID"), szID);

    // Create entries under CLSID.
    iStat |= SetKeyAndValue(szCLSID, NULL, szDesc);
    iStat |= SetKeyAndValue(szCLSID, _T("ProgID"), szProgID);
    iStat |= SetKeyAndValue(szCLSID, _T("VersionIndependentProgID"), szIndepProgID);
    iStat |= SetKeyAndValue(szCLSID, _T("NotInsertable"), NULL);

	// Set the server path.
    iStat |= SetKeyAndValue(szCLSID, _T("InprocServer32"), szModule);

	// Add the threading model information.
	_tcscpy_s(rcInproc, NumItems(rcInproc), szCLSID);
	_tcscat_s(rcInproc, NumItems(rcInproc), _T("\\InprocServer32"));
	iStat |= SetRegValue(rcInproc, _T("ThreadingModel"), _T("Apartment"));

	return iStat == 0 ? 0 : -1;
}


//*************************************************************************************************
// Unregister the basic information in the system registry for a given object class.
//*************************************************************************************************
int UnregisterClass(						// -1 or 0.
	REFCLSID	rclsid,						// Class ID we are registering.
	LPCTSTR		szProgID,					// Class prog ID.
	LPCTSTR		szIndepProgID)				// Class version independant prog ID.
{
	Trace::Write("Enter UnregisterClass");

	TCHAR		szID[64];					// The class ID to register.
	TCHAR		szCLSID[64];				// CLSID\\szID.

	// Create some base key strings.
    StringFromGUID2(rclsid, szID, NumItems(szID));
    _tcscpy_s(szCLSID, NumItems(szCLSID), _T("CLSID\\"));
	_tcscat_s(szCLSID, NumItems(szCLSID), szID);

	// Delete the version independant prog ID settings.
	DeleteKey(szIndepProgID, _T("CurVer"));
	DeleteKey(szIndepProgID, _T("CLSID"));
	RegDeleteKey(HKEY_CLASSES_ROOT, szIndepProgID);

	// Delete the prog ID settings.
	DeleteKey(szProgID, _T("CLSID"));
	RegDeleteKey(HKEY_CLASSES_ROOT, szProgID);

	// Delete the class ID settings.
	DeleteKey(szCLSID, _T("ProgID"));
	DeleteKey(szCLSID, _T("VersionIndependentProgID"));
	DeleteKey(szCLSID, _T("NotInsertable"));
	DeleteKey(szCLSID, _T("InprocServer32"));
	RegDeleteKey(HKEY_CLASSES_ROOT, szCLSID);
	return 0;
}

//*************************************************************************************************
// Remove the registry keys for a type library.
//*************************************************************************************************
int UnregisterTypeLib(						// -1 or 0
	REFGUID		rtlbid,						// TypeLib ID we are registering.
	LPCTSTR		szVersion)					// TypeLib version.
{
	Trace::Write("Enter UnregisterTypeLib");

	TCHAR		szID[64];					// The typelib ID to register.
	TCHAR		szTLBID[64];				// TypeLib\\szID.
	TCHAR		szTLBVersion[64];			// TypeLib\\szID\\szVersion

	// Create some base key strings.
    StringFromGUID2(rtlbid, szID, NumItems(szID));
	_tcscpy_s(szTLBID, NumItems(szTLBID), _T("TypeLib\\"));
	_tcscat_s(szTLBID, NumItems(szTLBID), szID);
	_tcscpy_s(szTLBVersion, NumItems(szTLBVersion), szTLBID);
	_tcscat_s(szTLBVersion, NumItems(szTLBVersion), _T("\\"));
	if (_tcscat_s(szTLBVersion, NumItems(szTLBVersion), szVersion) != 0)
	{
		return -1;
	}

    // Delete Typelib keys.
    DeleteKey(szTLBVersion, _T("HELPDIR"));
    DeleteKey(szTLBVersion, _T("FLAGS"));
    DeleteKey(szTLBVersion, _T("0\\win32"));
    DeleteKey(szTLBVersion, _T("0"));
    DeleteKey(szTLBID, szVersion);
    RegDeleteKey(HKEY_CLASSES_ROOT, szTLBID);
	return 0;
}

/* TODO: 
//*************************************************************************************************
// Convert a variant into an SBCS string.  Return an error if the type can not be coerced.
//*************************************************************************************************
HRESULT VariantToLPTSTR(					// Return status.
	VARIANT		&sVar,						// Variant to convert.
	__out_z __out_ecount(iSize) LPTSTR szStr, // Buffer to convert into.
	int			iSize)						// Size of the buffer.
{
	VARIANT		Tmp;						// Used for type coersion.
	HRESULT		hr;

	// Check for optional parameter.
	if (sVar.vt == VT_ERROR)
	{
		return sVar.scode;
	}

	// Make sure buffer has any size.
	if (iSize <= 0)
	{
		assert (FALSE);
		return E_FAIL;
	}

	// If there is no string, we are done.
	if (sVar.vt == VT_EMPTY)
	{
		*szStr = '\0';
		return (S_OK);
	}

	// Convert to a BSTR.
	VariantInit(&Tmp);
	if (FAILED(hr = VariantChangeType(&Tmp, &sVar, 0, VT_BSTR)))
	{
		return hr;
	}

	// Convert to SBCS.
	hr = BSTRToLPTSTR(Tmp.bstrVal, szStr, iSize);

	VariantClear(&Tmp);
	return hr;
}

//*************************************************************************************************
// Convert a Variant into a new LPTSTR.  This involves allocating the memory and converting from
// UNICODE to ansi.
//*************************************************************************************************
HRESULT VariantToNewLPTSTR(					// Return status.
	VARIANT		sStr,						// VARIANT to convert.
	__out_z __deref_out LPTSTR *pszStr)		// Buffer for ptr to new ansi str.
{
	VARIANT		Tmp;						// Used to convert to BSTR.
	HRESULT		hr;

	// Convert to a BSTR.
	VariantInit(&Tmp);
	if (FAILED(hr = VariantChangeType(&Tmp, &sStr, 0, VT_BSTR)))
	{
		return hr;
	}

	// Convert to an LPTSTR
	hr = BSTRToNewLPTSTR(Tmp.bstrVal, pszStr);
	VariantClear(&Tmp);
	return hr;
}

//*************************************************************************************************
// Convert a BSTR into a new LPTSTR.  This involves allocating the memory and converting from
// UNICODE to ansi.
//*************************************************************************************************
HRESULT BSTRToNewLPTSTR(					// Return status.
	BSTR		sStr,						// BSTR to convert.
	__out_z __deref_out LPTSTR *pszStr)		// Buffer for ptr to new ansi str.
{
	// Calculate the required Ansi string lenght and allocate it 
	int		iLen = 0;
	iLen = WideCharToMultiByte(CP_ACP, 0, sStr , -1, *pszStr, 0, NULL, NULL);
	if (0 == iLen)
	{
		return E_FAIL;
	}

	// add room for the null terminator
	iLen++;
	*pszStr = new TCHAR [iLen];
	if (*pszStr == NULL)
	{
		return E_OUTOFMEMORY;
	}

	// Convert to MBCS.
	HRESULT hr = BSTRToLPTSTR(sStr, *pszStr, iLen);
	if (FAILED(hr))
	{
		delete[] *pszStr;
		return hr;
	}

	return S_OK;
}

//*************************************************************************************************
// Converts a LPCTSTR into a new BSTR, returning the newly allocated BSTR in the given pointer
//*************************************************************************************************
HRESULT LPCTSTRToNewBSTR(				// Return status.
	BSTR		*psName,				// Return BSTR here.
	LPCTSTR		szStr)					// String to convert.
{
	WCHAR		*szUStr;				// UNICODE string for BSTR.
	int			iSize;					// Bytes required for conversion.

	// If it is an empty string or a NULL pointer, don't bother with the rest.
	if (NULL == szStr || '\0' == *szStr)
	{
		if ((*psName = SysAllocString(L"")) == NULL)
		{
			return E_OUTOFMEMORY;
		}
		return S_OK;
	}
	
	// Find the size up front.
	iSize = strlen(szStr);

	// Allocate a buffer for the conversion to UNICODE.
	if ((szUStr = new WCHAR[iSize + 1]) == NULL)
	{
		return E_OUTOFMEMORY;
	}

	// Convert to wide.
	if (MultiByteToWideChar(CP_ACP, 0, szStr, -1, szUStr, iSize + 1) == 0)
	{
		delete [] szUStr;
		return E_FAIL;
	}

	// Allocate the string and return.
	if ((*psName = SysAllocString(szUStr)) == NULL)
	{
		delete [] szUStr;
		return E_OUTOFMEMORY;
	}

	delete [] szUStr;
	return S_OK;
}
*/

//*************************************************************************************************
// Convert a variant to either an integer or a string.  This is used by collections to coerce the
// variant.
//*************************************************************************************************
HRESULT VariantToIntOrStr(
	VARIANT const *pVar,					// Variant to coerce
	LONG		*pl,						// resulting long, if *pfString==FALSE
	BSTR		*pbstr,						// resulting string, if *pfString==TRUE
	BOOL		*pfString)					// Was is string or integer?
{
	Trace::Write("Enter VariantToIntOrStr");

	VARIANT		varResult;
	HRESULT		hr;

	VariantInit(&varResult);

    // Try converting to both.  If it looks like a string, try
    // that first.  This makes sure that "123" doesn't become
    // 123.
	if ((V_VT(pVar) & ~VT_BYREF) == VT_BSTR)
	{
		hr = VariantChangeType(&varResult, (LPVARIANT) pVar, 0, VT_BSTR);
		if (FAILED(hr)) // Try integer.
		{
			hr = VariantChangeType(&varResult, (LPVARIANT) pVar, 0, VT_I4);
		}
	}
    else
	{
		hr = VariantChangeType(&varResult, (LPVARIANT) pVar, 0, VT_I4);
		if (FAILED(hr)) // Try string.
		{
			hr = VariantChangeType(&varResult, (LPVARIANT) pVar, 0, VT_BSTR);
		}
	}

	if (SUCCEEDED(hr))
	{
		if (V_VT(&varResult) == VT_I4)
		{
			*pl = V_I4(&varResult);
			*pfString = FALSE;
		}
		else
		{
			assert(V_VT(&varResult) == VT_BSTR);
			*pbstr = V_BSTR(&varResult);
			*pfString = TRUE;
		}
	}

	return hr;
}

//*************************************************************************************************
// Set an entry in the registry of the form: HKEY_CLASSES_ROOT\szKey\szSubkey = szValue.
// If szSubkey or szValue are NULL, omit them from the above expression.
//*************************************************************************************************
static BOOL SetKeyAndValue(					// TRUE or FALSE.
	LPCTSTR		szKey,						// Name of the reg key to set.
	LPCTSTR		szSubkey,					// Optional subkey of szKey.
	LPCTSTR		szValue)					// Optional value for szKey\szSubkey.
{
	Trace::Write("Enter SetKeyAndValue");

	UINT		iKey = szKey ? lstrlen(szKey) : 0;
	UINT		iSubkey = szSubkey ? lstrlen(szSubkey) : 0;

	// Buffer for the full key name.
	int iKeySize = iKey + 1 + iSubkey + 1;
	TCHAR* pcKey = new TCHAR[iKeySize];
	if (!pcKey)
	{
		return FALSE;
	}

	// Init the key with the base key name.
	_tcscpy_s(pcKey, iKeySize, szKey);

	// Append the subkey name (if there is one).
	if (szSubkey != NULL)
	{
		_tcscat_s(pcKey, iKeySize, _T("\\"));
		_tcscat_s(pcKey, iKeySize, szSubkey);
	}

	// Create the key and set its value.
	BOOL res = SetRegValue(pcKey, NULL, szValue);
	delete[] pcKey;
	return res;
}

//*************************************************************************************************
// Open the key, create a new keyword and value pair under it.
//*************************************************************************************************
static BOOL SetRegValue(					// Return status.
	LPCTSTR		szKeyName,					// Name of full key.
	LPCTSTR		szKeyword,					// Name of keyword.
	LPCTSTR		szValue)					// Value of keyword.
{
	Trace::Write("Enter SetRegValue");

	HKEY		hKey;						// Handle to the new reg key.

	// Create the registration key.
	if (RegCreateKeyEx(HKEY_CLASSES_ROOT, szKeyName, 0, NULL,
						REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL,
						&hKey, NULL) != ERROR_SUCCESS)
	{
		return FALSE;
	}

	// Set the value (if there is one).
	if (szValue != NULL)
	{
		if (RegSetValueEx(hKey, szKeyword, 0, REG_SZ, (BYTE *)szValue,
							(lstrlen(szValue)+1) * sizeof(TCHAR)) != ERROR_SUCCESS)
		{
			RegCloseKey(hKey);
			return FALSE;
		}
	}

	RegCloseKey(hKey);
	return TRUE;
}

//*************************************************************************************************
// Delete an entry in the registry of the form: HKEY_CLASSES_ROOT\szKey\szSubkey.
//*************************************************************************************************
static BOOL DeleteKey(						// TRUE or FALSE.
	LPCTSTR		szKey,						// Name of the reg key to set.
	LPCTSTR		szSubkey)					// Subkey of szKey.
{
	Trace::Write("Enter DeleteKey");

	UINT		iKey = szKey ? lstrlen(szKey) : 0;
	UINT		iSubkey = szSubkey ? lstrlen(szSubkey) : 0;

	// Buffer for the full key name.
	int iKeySize = iKey + 1 + iSubkey + 1;
	TCHAR* pcKey = new TCHAR[iKeySize];
	if (!pcKey)
	{
		return FALSE;
	}

	// Init the key with the base key name.
	_tcscpy_s(pcKey, iKeySize, szKey);

	// Append the subkey name (if there is one).
	if (szSubkey != NULL)
	{
		_tcscat_s(pcKey, iKeySize, _T("\\"));
		_tcscat_s(pcKey, iKeySize, szSubkey);
	}

	// Delete the registration key.
	RegDeleteKey(HKEY_CLASSES_ROOT, pcKey);

	delete[] pcKey;
	return TRUE;
}
